package com.luom.fsp.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luom.fsp.common.ErrorCode;
import com.luom.fsp.constant.CommonConstant;
import com.luom.fsp.exception.BusinessException;
import com.luom.fsp.exception.ThrowUtils;
import com.luom.fsp.mapper.CouponMapper;
import com.luom.fsp.model.dto.coupon.CouponQueryRequest;
import com.luom.fsp.model.entity.Coupon;
import com.luom.fsp.model.entity.UserCoupon;
import com.luom.fsp.model.enums.CouponTypeEnum;
import com.luom.fsp.model.vo.CouponVO;
import com.luom.fsp.service.CouponService;
import com.luom.fsp.service.UserCouponService;
import com.luom.fsp.service.UserService;
import com.luom.fsp.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.luom.fsp.constant.RedisConstant.SECKILL_COUPON_STOCK_KEY;

/**
 * 优惠券服务实现
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 校验数据
     *
     * @param coupon
     * @param add    对创建的数据进行校验
     */
    @Override
    public void validCoupon(Coupon coupon, boolean add) {
        ThrowUtils.throwIf(coupon == null, ErrorCode.PARAMS_ERROR);

        Long id = coupon.getId();
        String name = coupon.getName();
        String type = coupon.getType();
        BigDecimal discountAmount = coupon.getDiscountAmount();
        BigDecimal minAmount = coupon.getMinAmount();
        Integer stock = coupon.getStock();
        Date startTime = coupon.getStartTime();
        Date endTime = coupon.getEndTime();

        // 创建数据时，参数不能为空
        if (add) {
            ThrowUtils.throwIf(StringUtils.isBlank(name), ErrorCode.PARAMS_ERROR, "优惠券名称不能为空");
            ThrowUtils.throwIf(StringUtils.isBlank(type), ErrorCode.PARAMS_ERROR, "优惠券类型不能为空");
            ThrowUtils.throwIf(discountAmount == null, ErrorCode.PARAMS_ERROR, "优惠不能为空");
            ThrowUtils.throwIf(minAmount == null, ErrorCode.PARAMS_ERROR, "优惠券门槛不能为空");
            ThrowUtils.throwIf(stock == null, ErrorCode.PARAMS_ERROR, "优惠券库存不能为空");
            ThrowUtils.throwIf(startTime == null, ErrorCode.PARAMS_ERROR, "优惠券开始时间不能为空");
            ThrowUtils.throwIf(endTime == null, ErrorCode.PARAMS_ERROR, "优惠券结束时间不能为空");
        }
        // 修改数据时，有参数则校验
        if (StringUtils.isNotBlank(name)) {
            ThrowUtils.throwIf(name.length() > 80, ErrorCode.PARAMS_ERROR, "名称过长");
        }
        // 校验优惠券类型有效性
        if (StringUtils.isNotBlank(type)) {
            List<String> values = CouponTypeEnum.getValues();
            boolean validType = values.contains(type);
            ThrowUtils.throwIf(!validType, ErrorCode.PARAMS_ERROR, "无效的优惠券类型");
        }

        // 校验金额有效性
        if (discountAmount != null) {
            ThrowUtils.throwIf(discountAmount.compareTo(BigDecimal.ZERO) < 0,
                    ErrorCode.PARAMS_ERROR, "优惠金额不能为负数");
        }
        if (minAmount != null) {
            ThrowUtils.throwIf(minAmount.compareTo(BigDecimal.ZERO) < 0,
                    ErrorCode.PARAMS_ERROR, "优惠门槛金额无效");
        }

        // 校验金额逻辑关系
        if (discountAmount != null && minAmount != null) {
            ThrowUtils.throwIf(discountAmount.compareTo(minAmount) >= 0,
                    ErrorCode.PARAMS_ERROR, "优惠金额不能大于等于门槛金额");
        }

        // 校验库存
        if (stock != null) {
            ThrowUtils.throwIf(stock < 0, ErrorCode.PARAMS_ERROR, "库存不能为负数");
        }

        // 校验时间有效性
        if (startTime != null && endTime != null) {
            ThrowUtils.throwIf(!startTime.before(endTime),
                    ErrorCode.PARAMS_ERROR, "开始时间必须早于结束时间");
        }
    }

    /**
     * 获取查询条件
     *
     * @param couponQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Coupon> getQueryWrapper(CouponQueryRequest couponQueryRequest) {
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<>();
        if (couponQueryRequest == null) {
            return queryWrapper;
        }
        Long id = couponQueryRequest.getId();
        String name = couponQueryRequest.getName();
        String type = couponQueryRequest.getType();
        BigDecimal discountAmount = couponQueryRequest.getDiscountAmount();
        BigDecimal minAmount = couponQueryRequest.getMinAmount();
        Integer stock = couponQueryRequest.getStock();
        Date startTime = couponQueryRequest.getStartTime();
        Date endTime = couponQueryRequest.getEndTime();
        Integer isSeckill = couponQueryRequest.getIsSeckill();
        String sortField = couponQueryRequest.getSortField();
        String sortOrder = couponQueryRequest.getSortOrder();

        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(name), "name", name);

        // 精确查询
        queryWrapper
                .eq(id != null, "id", id)
                .eq(StringUtils.isNotBlank(type), "type", type)
                .eq(discountAmount != null, "discountAmount", discountAmount) // 注意数据库字段命名
                .eq(minAmount != null, "minAmount", minAmount)
                .eq(stock != null, "stock", stock);

        // 时间相关,大于startTime，小于endTime
        queryWrapper
                .ge(startTime != null, "startTime", startTime)
                .le(endTime != null, "endTime", endTime);

        // 秒杀券
        if (isSeckill != null && isSeckill == 1) {
            queryWrapper.isNotNull("seckillStartTime");
            queryWrapper.isNotNull("seckillEndTime");
        }

        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取优惠券封装
     *
     * @param coupon
     * @param request
     * @return
     */
    @Override
    public CouponVO getCouponVO(Coupon coupon, HttpServletRequest request) {
        // 对象转封装类
        return CouponVO.objToVo(coupon);
    }

    /**
     * 分页获取优惠券封装
     *
     * @param couponPage
     * @param request
     * @return
     */
    @Override
    public Page<CouponVO> getCouponVOPage(Page<Coupon> couponPage, HttpServletRequest request) {
        List<Coupon> couponList = couponPage.getRecords();
        Page<CouponVO> couponVOPage = new Page<>(couponPage.getCurrent(), couponPage.getSize(), couponPage.getTotal());
        if (CollUtil.isEmpty(couponList)) {
            return couponVOPage;
        }
        // 对象列表 => 封装对象列表
        List<CouponVO> couponVOList = couponList.stream().map(CouponVO::objToVo).collect(Collectors.toList());
        couponVOPage.setRecords(couponVOList);
        return couponVOPage;
    }


    /**
     * 获取用户可购买的优惠券
     *
     * @param userId   用户id
     * @param current  当前页数
     * @param pageSize 每页大小
     * @return
     */
    //原方案：2次数据库查询 + 内存过滤 → O(n)时间复杂度
    //新方案：1次数据库查询 → O(1)时间复杂度
    //确保user_coupon表有(userId, couponId)的联合索引
    @Override
    public Page<CouponVO> getListCanBuyCoupon(Long userId, long current, long pageSize) {

        QueryWrapper<Coupon> couponQueryWrapper = new QueryWrapper<>();
        // 筛选未过期的优惠券
        couponQueryWrapper.le("startTime", new Date());
        couponQueryWrapper.gt("endTime", new Date());
        couponQueryWrapper.gt("stock", 0);
        // 如果是秒杀券，检验秒杀时间
        couponQueryWrapper.and(wrapper -> wrapper
                .isNull("seckillStartTime")
                .or()
                .lt("seckillStartTime", new Date())
                .gt("seckillEndTime", new Date())
        );

        // 排除用户已购买的优惠券（使用子查询提升性能）
        couponQueryWrapper.notInSql("id",
                "SELECT couponId FROM user_coupon WHERE userId = " + userId);

        // 执行分页查询
        Page<Coupon> couponPage = this.page(new Page<>(current, pageSize), couponQueryWrapper);
        return this.getCouponVOPage(couponPage, null);
    }

    /**
     * 获取秒杀优惠券
     *
     * @param userId
     * @param current
     * @param pageSize
     * @return
     */
    @Override
    public Page<CouponVO> getListSeckillCoupon(Long userId, long current, long pageSize) {
        if (userId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 1. 获取当前时间
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        LocalDateTime now = LocalDateTime.now(zoneId);

        // 2. 构建查询条件
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<>();
        queryWrapper.le("seckillStartTime", now)
                .ge("seckillEndTime", now)
                .gt("stock", 0);
        queryWrapper.notInSql("id",
                "SELECT couponId FROM user_coupon WHERE userId = " + userId);
        queryWrapper.orderByDesc("createTime");
        Page<Coupon> couponPage = this.page(new Page<>(current, pageSize), queryWrapper);
        List<CouponVO> collect = couponPage.getRecords().stream().map(CouponVO::objToVo).collect(Collectors.toList());
        Page<CouponVO> couponVOPage = new Page<>();
        couponVOPage.setRecords(collect);
        return couponVOPage;
    }

    @Override
    public long addCoupon(Coupon coupon) {
        // 数据校验
        this.validCoupon(coupon, true);
        // 写入数据库
        boolean result = this.save(coupon);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回新写入的数据 id
        long newCouponId = coupon.getId();
        // 如果是秒杀券，库存写入redis
        /*if (coupon.getSeckillStartTime() != null && coupon.getSeckillEndTime() != null) {
            stringRedisTemplate.opsForValue().set(SECKILL_COUPON_STOCK_KEY+ coupon.getId(), String.valueOf(coupon.getStock()));
        }*/

        return newCouponId;
    }
    /*@Override
    public Page<CouponVO> getListCanBuyCoupon(Long userId, long current, long pageSize) {
        if (userId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<Coupon> couponQueryWrapper = new QueryWrapper<>();
        couponQueryWrapper.ge("endTime", new Date());
        // 找出来用户已购买的优惠卷
        QueryWrapper<UserCoupon> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        Page<UserCoupon> page = userCouponService.page(new Page<>(current, pageSize), queryWrapper);
        if (page != null){
            List<UserCoupon> userCoupons = page.getRecords();
            List<Long> userHasCouponIds = userCoupons.stream().map(UserCoupon::getCouponId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(userHasCouponIds)){
                couponQueryWrapper.notIn("id", userHasCouponIds);
                Page<Coupon> couponPage = this.page(new Page<>(current, pageSize), couponQueryWrapper);
                return this.getCouponVOPage(couponPage, null);
            }
        }
        if (page == null){
            Page<Coupon> couponPage = this.page(new Page<>(current, pageSize), couponQueryWrapper);
            return this.getCouponVOPage(couponPage, null);
        }

        return null;
    }*/

}
